vert = 
"""
precision highp float;
attribute vec2 vertex; 
attribute vec2 vertexCoord; 


uniform mat3 worldTransform;  
uniform vec4 screen;
uniform vec2 spriteSheetSize;

varying highp vec2 coord;

void main() { 
  vec3 homogeniousCoord = vec3(vertex, 1.0);
  gl_Position = vec4(homogeniousCoord * worldTransform, 1.0) * screen; 
  coord = vertexCoord / spriteSheetSize;
}
"""

frag = 
""" 
precision highp float;
varying highp vec2 coord;
uniform sampler2D spriteTexture; 
void main() { 
  gl_FragColor = texture2D(spriteTexture, coord) * vec4(1.0, 1.0, 1.0, 1.0); 
}
"""

class window.Tilemap
  constructor : (@g, @xtiles, @ytiles) ->
    @tiles = {}

    @buffTiles = @g.ctx.MeshBuffer(20000)
    @buffWalls = @g.ctx.MeshBuffer(20000)

    @buffTiles.addDescription "vertex", "FLOAT", 2
    @buffTiles.addDescription "vertexCoord", "FLOAT", 2

    @buffWalls.addDescription "vertex", "FLOAT", 2
    @buffWalls.addDescription "vertexCoord", "FLOAT", 2

    @prog = @g.ctx.Program()
    @prog.setFragSource frag
    @prog.setVertSource vert
    @prog.link()

    @tex = @g.ctx.Texture()

    @tex.load("img/sprites.png")
    
  setTile : (x, y, t) ->
    @tiles[[x, y]] = t

  intersectSolid : (x, y, e) ->
    x += e.boffx()
    y += e.boffy()
    w = e.bwidth()
    h = e.bheight()

    t0 = @getTileDirect(x, y)
    t1 = @getTileDirect(x + w, y)
    t2 = @getTileDirect(x, y + h)
    t3 = @getTileDirect(x + w, y + h)
    out = []
    out.push t0 if t0 != undefined and t0 != null and t0.solid()
    out.push t1 if t1 != undefined and t1 != null and t1.solid()
    out.push t2 if t2 != undefined and t2 != null and t2.solid()
    out.push t3 if t3 != undefined and t3 != null and t3.solid()
    return out

  simpleIntersectSolid : (x, y, e) ->
    x += e.boffx()
    y += e.boffy()
    w = e.bwidth()
    h = e.bheight()

    t0 = @getTileDirect(x, y)
    t1 = @getTileDirect(x + w, y)
    t2 = @getTileDirect(x, y + h)
    t3 = @getTileDirect(x + w, y + h)
    if t0 != undefined and t0 != null and t0.solid()
      return true
    if t1 != undefined and t1 != null and t1.solid()
      return true
    if t2 != undefined and t2 != null and t2.solid()
      return true
    if t3 != undefined and t3 != null and t3.solid()
      return true
    return false

  intersectNonSolid : (x, y, e) ->
    x += e.boffx()
    y += e.boffy()
    w = e.bwidth()
    h = e.bheight()

    t0 = @getTileDirect(x, y)
    t1 = @getTileDirect(x + w, y)
    t2 = @getTileDirect(x, y + h)
    t3 = @getTileDirect(x + w, y + h)

    out = []
    out.push t0 if t0 != undefined and t0 != null and not t0.solid()
    out.push t1 if t1 != undefined and t1 != null and not t1.solid()
    out.push t2 if t2 != undefined and t2 != null and not t2.solid()
    out.push t3 if t3 != undefined and t3 != null and not t3.solid()
    return out

  getTile : (x, y) ->
    @tiles[[x, y]]

  isSolid : (x, y) ->
    t = @getTile x, y
    return false if t == null
    return t.solid()

  generateTileMesh : () ->
    @buffTiles.clear()
    @buffWalls.clear()

    console.log "Regenerating tiles"

    i = 0
    buffTiles = @buffTiles
    buffWalls = @buffWalls
    genTile = (x, y, u, v) ->
      buffTiles.pushff x * 64, y * 64
      buffTiles.pushff u, v
      
      buffTiles.pushff x * 64 + 64, y * 64
      buffTiles.pushff u + 16, v
      
      buffTiles.pushff x * 64 + 64, y * 64 + 64
      buffTiles.pushff u + 16, v + 16
      
      buffTiles.pushff x * 64, y * 64 + 64 
      buffTiles.pushff u, v + 16

      buffTiles.pushiii i, i + 1, i + 2
      buffTiles.pushiii i + 2, i + 3, i
      i += 4


    n = 0
    genWalls = (x, y, u, v) ->
      buffWalls.pushff x * 64, y * 64
      buffWalls.pushff u, v
      
      buffWalls.pushff x * 64 + 64, y * 64
      buffWalls.pushff u + 16, v
      
      buffWalls.pushff x * 64 + 64, y * 64 + 64
      buffWalls.pushff u + 16, v + 16
      
      buffWalls.pushff x * 64, y * 64 + 64 
      buffWalls.pushff u, v + 16

      buffWalls.pushiii n, n + 1, n + 2
      buffWalls.pushiii n + 2, n + 3, n
      n += 4

    for k, v of @tiles        
      continue if v == null
      continue if v.canSee == false
      v.createTile genTile, genWalls

    @buffWalls.upload()
    @buffTiles.upload()

  render : () ->
    @g.ctx.gl.lineWidth 1.0
    @prog.use()
    @prog.u2f "spriteSheetSize", @tex.width, @tex.height
    @prog.um3f "worldTransform", @g.viewport.getMatrix()
    @prog.attachTexture "spriteTexture", 1, @tex

    @buffTiles.setup @prog
    @buffTiles.draw()

    @buffWalls.setup @prog
    @buffWalls.draw()
  
  addTileIfNotPresent : (x, y) ->
    tile = @getTile x, y
    if tile == undefined or tile == null
      @setTile x, y, new window.Tile(@g, x, y, @)


  fillTilesIfNotThere : (x0, y0, w, h, spr) ->
    for x in [x0..x0 + w]
      for y in [y0..y0 + h]
        @addTileIfNotPresent x, y
        
        if spr != undefined
          t = @getTile x, y
          t.tileSpr = spr

    undefined


  getTileDirect : (x, y) ->
    return @getTile (x / 64) | 0, (y / 64) | 0


  makeWall : (x, y) ->
    @addTileIfNotPresent x, y
    t = @getTile x, y

    if t.open != undefined
      return

    t.hasWall = true if t != null and t != undefined

  removeWall : (x, y) ->
    t = @getTile x, y
    t.hasWall = false if t != null

  createDoorTile : (x, y) ->
    t = new window.Door(@g, x, y, @)
    
    t0 = @getTile x, y
    if t0 != undefined
      t.tileSpr = t0.tileSpr

    @setTile x, y, t
    return t

  createTableTile : (x, y) ->
    t = new window.Table(@g, x, y, @)
    
    t0 = @getTile x, y

    if t0 != undefined
      t.tileSpr = t0.tileSpr

    @setTile x, y, t
    return t

  setTileSprite : (x, y, spr) ->
    t = @getTile x, y
    t.tileSpr = spr if t != null


  pathFromTo : (e, x1, y1) ->
    
    x0 = e.x
    y0 = e.y
    
    if @intersectSolid(x0, y0, e).length != 0
      return null

      
    me = @
    h = []

    lookup = {}
    closest = 1000000000
    close = null

    dist = (x, y) ->
      return Math.abs(x0 - x) + Math.abs(y0 - y)

    add = (x, y, c, l) ->
      if dist(x, y) > 750
        return

      if lookup[[x, y]] == true
        return
      
      if me.intersectSolid(x, y, e).length != 0
        return 

      lookup[[x, y]] = true

      dx = x - x1
      dy = y - y1
      d = dx * dx + dy * dy
      
      node =  [x, y, c, l]
      if d < closest
        close = node
        closest = d

      h.push node
      h.sort (x, y) -> x[2] < y[2]

    constructPath = (node) ->
      out = []
      while node != null
        [x, y, c, l] = node
        out.push [x, y]
        node = l
      return out

    stepSize = 20

    h.push [x0, y0, 0, null]
    while h.length != 0
      node = h.splice h.length - 1, 1
      node = node[0]

      [x, y, c, last] = node      
      if Math.abs(x - x1) + Math.abs(y - y1) < stepSize * 2
        return constructPath node 
      
      add x + stepSize, y, c + stepSize, node
      add x, y + stepSize, c + stepSize, node
      add x - stepSize, y, c + stepSize, node
      add x, y - stepSize, c + stepSize, node
    return constructPath node

  makeFence : (x, y) ->
    t = new window.FenceTile(@g, x, y, @)

    tt = @getTile x, y
    if tt != null and t != undefined
      t.tileSpr = tt.tileSpr
    
    @setTile x, y, t

  makeExitTile : (x, y) ->
    t = new window.ExitTile(@g, x, y, @)

    tt = @getTile x, y
    if tt != null and t != undefined
      t.tileSpr = tt.tileSpr
    
    @setTile x, y, t
